
#import <appkit/appkit.h>
#import <dpsclient/wraps.h>
#import <libc.h>
#import <math.h>

#import "SpiralView.h"
#import "SpiralWraps.h"
#import "alphanum.h"
#import "Thinker.h"

#define MAXCOLORS 14
static struct color {
	float r, g, b;
} colorChanges[MAXCOLORS] = {
	/* color gradient */
	{1.0, 1.0, 1.0},	/* white */
	{1.0, 0.6, 1.0},	/* pink */
	{1.0, 0.1, 1.0},	/* fuschia */
	{0.4, 0.0, 1.0},	/* purple */
	{0.0, 0.0, 1.0},	/* blue */
	{0.0, 0.3, 1.0},	/* blue cyan */
	{0.0, 1.0, 1.0},	/* cyan */
	{0.0, 1.0, 0.0},	/* green */
	{0.7, 1.0, 0.0},	/* yellow green */
	{1.0, 1.0, 0.0},	/* yellow */
	{1.0, 0.7, 0.0},	/* yellow orange */
	{1.0, 0.3, 0.0},	/* orange */
	{1.0,  0.0, 0.0},	/* red */
	{0.75, 0.0, 0.0},	/* dark red */
};

// The values MUST be unique, else all array elements are pointers
//		to the exact same value!
static NXDefaultsVector	spiralDefaults = {
	{"SpiralMaxCount", "0000"}
	,{"SpiralMinRadius", "0001"}
	,{"SpiralMaxRadius", "0002"}
	,{NULL}
};


@implementation SpiralView

- initFrame:(const NXRect *)frameRect
{
	[super initFrame:frameRect];

	// initialize variables (many will be init via startOver)
	degCircles = 3;
	colorIndex = 0;
	[self loadDefaults];

	[self startOver];

	return self;
}

//**********
- oneStep
{
	if (numSpirals >= maxSpirals) {
		[self startOver];
		[self display];
		numSpirals = 0;
	}

	// draw new arc
	PSLSetColor (colorChanges[colorIndex].r - (degree/maxDegree), colorChanges[colorIndex].g - (degree/maxDegree), colorChanges[colorIndex].b - (degree/maxDegree));
	PSLDrawArc(x, y, radius, degree);

	radius -= (maxCurrentRadius / maxDegree);
	degree += 1.0;
	degIterations++;
	if ((degIterations > (maxDegree)) || (radius < 1))
		[self newSpiral];

	return self;
}

//**********
- (const char *)windowTitle
{	return "Spiral Generator";
}

- inspector:sender
{
	char buf[MAXPATHLEN];

	if (!inspectorPanel) {
		sprintf(buf,"%s/Spiral.nib",[sender moduleDirectory:"Spiral"]);
		[NXApp loadNibFile:buf owner:self withNames:NO];

		// validate that defaults are within UI constraints
		[self constrainDefaults];

		// set UI Object values
		[minRadiusSlider setIntValue:minRadius];
		[minRadiusField  setIntValue:minRadius];
		[maxRadiusSlider setIntValue:maxRadius];
		[maxRadiusField  setIntValue:maxRadius];
		[maxSpiralsSlider setIntValue:maxSpirals];
		[maxSpiralsField  setIntValue:maxSpirals];
	}
	return inspectorPanel;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];
	[self startOver];
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
	if (!rects || !rectCount) return self;
	
	PSsetgray(NX_BLACK);
	NXRectFill(rects);
	return self;
}

- (BOOL) useBufferedWindow;
{	return YES;
}

//**********
- changeMinRadius:sender
{
	int temp;

	if (sender == minRadiusField) {
		temp = [sender intValue];
		if (temp < [minRadiusSlider minValue])
			temp = minRadius;
		if (temp > [minRadiusSlider maxValue])
			temp = minRadius;
		[sender setIntValue:temp];
	}

	minRadius = [sender floatValue];
	if (minRadius > maxRadius) {
		minRadius = maxRadius;
		[minRadiusSlider setIntValue:minRadius];
	}
	[minRadiusField setIntValue:minRadius];
	[minRadiusSlider setIntValue:minRadius];

	[self saveDefaults];
	return self;
}

- changeMaxRadius:sender
{
	int temp;

	if (sender == maxRadiusField) {
		temp = [sender intValue];
		if (temp < [maxRadiusSlider minValue])
			temp = maxRadius;
		if (temp > [maxRadiusSlider maxValue])
			temp = maxRadius;
		[sender setIntValue:temp];
	}

	maxRadius = [sender floatValue];
	if (minRadius > maxRadius) {
		maxRadius = minRadius;
		[maxRadiusSlider setIntValue:maxRadius];
	}
	[maxRadiusField setIntValue:maxRadius];
	[maxRadiusSlider setIntValue:maxRadius];

	[self saveDefaults];
	return self;
}

- changeMaxSpirals:sender
{
	int temp;

	if (sender == maxSpiralsField) {
		temp = [sender intValue];
		if (temp < [maxSpiralsSlider minValue])
			temp = [maxSpiralsSlider minValue];
		if (temp > [maxSpiralsSlider maxValue])
			temp = [maxSpiralsSlider maxValue];
		[sender setIntValue:temp];
	}

	maxSpirals = [sender floatValue];
	[maxSpiralsField setIntValue:maxSpirals];
	[maxSpiralsSlider setIntValue:maxSpirals];

	[self saveDefaults];
	return self;
}

//**********
- (void)loadDefaults
{
	itoa(MAXSPIRALS, spiralDefaults[0].value);
	itoa(MINRADIUS, spiralDefaults[1].value);
	itoa(MAXRADIUS, spiralDefaults[2].value);
	NXRegisterDefaults("BackSpace", spiralDefaults);

	maxSpirals = atoi((char*)NXGetDefaultValue("BackSpace", "SpiralMaxCount"));
	minRadius = atoi((char*)NXGetDefaultValue("BackSpace", "SpiralMinRadius"));
	maxRadius = atoi((char*)NXGetDefaultValue("BackSpace", "SpiralMaxRadius"));
}

- (void)saveDefaults
{
	itoa(maxSpirals, spiralDefaults[0].value);
	itoa(minRadius, spiralDefaults[1].value);
	itoa(maxRadius, spiralDefaults[2].value);
	NXWriteDefaults("BackSpace", spiralDefaults);
}

- (void)constrainDefaults
{
	// validate that defaults are within UI constraints
	if (minRadius < [minRadiusSlider minValue])
		minRadius = [minRadiusSlider minValue];
	if (minRadius > [minRadiusSlider maxValue])
		minRadius = [minRadiusSlider maxValue];

	if (maxRadius < [maxRadiusSlider minValue])
		maxRadius = [maxRadiusSlider minValue];
	if (maxRadius > [maxRadiusSlider maxValue])
		maxRadius = [maxRadiusSlider maxValue];

	if (minRadius > maxRadius)
		minRadius = maxRadius;

	if (maxSpirals < [maxSpiralsSlider minValue])
		maxSpirals = [maxSpiralsSlider minValue];
	if (maxSpirals > [maxSpiralsSlider maxValue])
		maxSpirals = [maxSpiralsSlider maxValue];
}

//**********
- startOver
{
	numSpirals = 0;
	maxDegree = 360 * degCircles;
	[self newSpiral];

	return self;
}

- newSpiral
{
	NXRect	rBounds;
	NXSize	rSize;

	numSpirals++;
	degree = 0;
	degIterations = 0;

	[self getBounds:&rBounds];
	rSize = rBounds.size;

	x = randBetween(0, rSize.width);
	y = randBetween(0, rSize.height);
	radius = randBetween(minRadius, maxRadius);
	maxCurrentRadius = radius;
	if (++colorIndex >= MAXCOLORS)
		colorIndex = 0;

	return self;
}

@end
